//
// Created by YueChuan on 2023/4/27.
//

#ifndef __YUECHUAN_BYTEARRAY_H
#define __YUECHUAN_BYTEARRAY_H

#include <memory>
#include <cstdint>
#include <string>
#include <cstring>
#include <fstream>
#include <math.h>
#include <vector>
#include <socket.h>
#include <iomanip>
#include "log.h"

namespace yuechuan {
    /**
     * @brief 二进制数组
     * @details 进行序列化和反序列化
     */
    class ByteArray {
    public:
        typedef std::shared_ptr<ByteArray> ptr;

        struct Node {
            /**
             * @brief 含参构造函数
             * @param size 指定内存块字节数
             */
            Node(size_t size);

            /**
             * @brief 无参构造函数
             */
            Node();

            /**
             * @brief 析构函数，释放内存
             */
            ~Node();

            /// 内存块地址
            char *ptr;
            /// 链表的下一个节点
            Node *next;
            /// 内存块大小
            size_t size;
        };

        /**
         * @brief 含参构造函数
         * @details 使用指定长度的内存块来构造ByteArray，默认为4k
         * @param base_size 内存块大小
         */
        ByteArray(size_t base_size = 4096);

        /**
         * @brief 析构函数
         */
        ~ByteArray();

        /**
         * @brief write fix int8 写入固定长度int8_t类型的数据
         * @param value要写入的数据
         */
        void writeFint8(int8_t value);

        /**
         * @brief write fix uint_8 写入固定长度为uint8_t类型的数据
         * @param value 要写入的数据
         */
        void writeFuint8(uint8_t value);

        /**
         * @brief write fix int16 (大端/小端)写入固定长度int8_t类型的数据
         * @param value要写入的数据
         */
        void writeFint16(int16_t value);

        /**
         * @brief write fix uint_16 (大端/小端)写入固定长度为uint8_t类型的数据
         * @param value 要写入的数据
         */
        void writeFuint16(uint16_t value);

        /**
       * @brief write fix int32 (大端/小端)写入固定长度int8_t类型的数据
       * @param value要写入的数据
       */
        void writeFint32(int32_t value);

        /**
         * @brief write fix uint_32 (大端/小端)写入固定长度为uint8_t类型的数据
         * @param value 要写入的数据
         */
        void writeFuint32(uint32_t value);

        /**
       * @brief write fix int64 (大端/小端)写入固定长度int8_t类型的数据
       * @param value要写入的数据
       */
        void writeFint64(int64_t value);

        /**
         * @brief write fix uint_64 (大端/小端)写入固定长度为uint8_t类型的数据
         * @param value 要写入的数据
         */
        void writeFuint64(uint64_t value);

        /**
         * @brief 写入有符号的Varint32类型的变量
         * details
                    * 实际调用uint32_t类型
                    * 需要使用DecodeZigzag64将有符号数转换为无符号数
         * @param value 要写入的数据
         */
        void writeInt32(int32_t value);

        /**
         * @brief 写入Varuint32_t类型数据
         * @details
                    * Varint编码
                    * 从低位到高位每次读取一个7位
                    * 然后再最高位加上标志位，构成一个字节
                    * 然后从高到低放置
                    * 对于uint32_t类型的数据最坏需要5个字节，最好只需要一个字节，取决于二进制位中1的个数
         * @param value 要写入的数据
         */
        void writeUint32(uint32_t value);

        /**
         * @brief 写入int64_t类型的数据
         * @details
                    * 实际调用uint64_t类型
                    * 需要使用DecodeZigzag64将有符号数转换为无符号数
         * @param value 要写入的数据
         */
        void writeInt64(int64_t value);

        /**
         * @brief 写入uint64_t类型的数据
         * @details
                    * Varint编码
                    * 从低位到高位每次读取一个7位
                    * 然后再最高位加上标志位，构成一个字节
                    * 然后从高到低放置
                    * 对于uint64_t类型的数据最坏需要10个字节，最好只需要1个字节，取决于二进制位中1的个数
         * @param value 要写入的数据
         */
        void writeUint64(uint64_t value);

        /**
         * @brief 写入float类型的数据
         * @details 实际上调用的是int64_t类型
         * @param value 要写入的数据
         */
        void writeFloat(float value);

        /**
         * @brief 写入double类型的数据
         * @details 实际上调用的是uint64_t类型
         * @param value 要写入的数据
         */
        void writeDouble(double value);

        /**
         * @brief 写入std::string类型的数据，用uint16_t作为长度类型
         * @param value 要写入的数据
         */
        void writeStringF16(const std::string &value);

        /**
         * @brief 写入std::string类型的数据，用uint32_t作为长度类型
         * @param value 要写入的数据
         */
        void writeStringF32(const std::string &value);

        void writeStringF64(const std::string &value);

        /**
         * @brief写入std::string 类型的数据，用Varint64作为长度乐行
         * @param value 要写入的数据
         */
        void writeStringVint(const std::string &value);

        /**
         * @brief 写入std::string类型的数据，无长度
         * @param value 要写入的数据
         */
        void writeStringWithoutLength(const std::string &value);

        /**
         * @brief 读取int8_t类型的数据
         * @return
         */
        int8_t readFint8();

        /**
         * @brief 读取uint8_t类型的数据
         * @return
         */
        uint8_t readFuint8();

        /**
         * @brief 读取int16_t类型的数据
         * @return
         */
        int16_t readFint16();

        /**
         * @brief 读取uint16_t类型的数据
         * @return
         */
        uint16_t readFuint16();

        /**
         * @brief 读取int32_t类型的数据
         * @return
         */
        int32_t readFint32();

        /**
         * @brief 读取uint32_t类型的数据
         * @return
         */
        uint32_t readFuint32();

        /**
         * @brief 读取int64_t类型的数据
         * @return
         */
        int64_t readFint64();

        /**
         * @brief 读取uint64_t类型的数据
         * @return
         */
        uint64_t readFuint64();


        /**
         * @brief 读取Varint 32位类型的数据
         * @details
                    * 将其转换为不带符号位的32位
                    * 调用DecodeZigzag算法
         * @return
         */
        int32_t readInt32();

        /**
         * @brief 读取不带符号位的Varint32类型的数据
         * @details
                    * 每次读取一个字节
                    * 将其从低位到高位每次取出7位二进制位
                    * 然后在最高位加上标志位
                    * 从高到低进行放置，将结果返回
         * @return
         */
        uint32_t readUint32();

        /**
         * @brief 读取Varint 64位的数据
         * @details
                    * 将其转换为不带符号位的64位
                    * 嗲用DecodeZigzag算法
         * @return
         */
        int64_t readInt64();

        /**
         * @brief 读取不带符号位Varint 64位的数据
         * @details
                    * 每次读取一个字节
                    * 将其从低位到高位每次取出7位二进制位
                    * 然后在最高位加上标志位
                    * 从高到低进行放置，将结果返回
         * @return
         */
        uint64_t readUint64();

        /**
         * @brief 读取float类型的数据
         * @details
                    * 实际调用的数uint32_t的读取
                    * float占用的字节数为4
         * @return
         */
        float readFloat();

        /**
         * @brief 读取double类型的数据
         * @details
                     * 实际上调用的数uint64_t的读取
                     * double占用的字节数为8
         * @return
         */
        double readDouble();

        /**
         * @brief 从字节数组中读取16位
         * @return
         */
        std::string readStringF16();

        /**
         * @brief 从字节数组中读取32位
         * @return
         */
        std::string readStringF32();

        /**
         * @brief 从字节数组中读取64位
         * @return
         */
        std::string readStringF64();

        /**
         * @brief 从字节数组中读取Varint64位
         * @return
         */
        std::string readStringVint();

        /**
         * @brief 清空内存块
         * @details
                    * 将当前操作的位置，数据大小赋值为0
                    * 将容量赋值为m_baseSize
                    * 将节点仅仅保留根节点
         */
        void clear();

        /**
         * @brief 写入数据
         * @param buf 要写入的数据指针
         * @param size 要写人的字节数
         */
        void write(const void *buf, size_t size);


        /**
         * @brief 从m_position位置进行读取
         * @param buf 存储要读取的字节
         * @param size 要读取的字节数
         */
        void read(void *buf, size_t size);

        /**
         * @brief 从指定位置进行读取
         * @param buf 存储要读取的字节
         * @param size 要读取的字节数
         * @param position 开始读取的位置
         */
        void read(void *buf, size_t size, size_t position) const;

        /**
         * @brief 获取当前操作节点的位置
         * @return
         */
        size_t getPosition() const { return m_position; }

        /**
         * @brief 设置当前位置
         * @details
                    * 首先要确保当前位置要小于容量大小即m_position < m_capacity
                    * 更新从m_position 减去 节点的大小来从而做到更新m_cur
         * @param position
         */
        void setPosition(size_t position);

        /**
         * @brief 写入字节到文件中
         * @param name 文件名称
         * @return
         */
        bool writeToFile(const std::string &name) const;

        /**
         * @brief 从文件中读取字节
         * @param name 文件名称
         * @return
         */
        bool readFromFile(const std::string &name);

        /**
         * @brief 获取内存块的大小
         * @return
         */
        size_t getBaseSize() const { return m_baseSize; }

        /**
         * @brief 获取当前数据的大小
         * @return
         */
        size_t getSize() const { return m_size; }

        /**
         * @brief 剩余未读取的大小
         * @return
         */
        size_t getReadSize() const { return m_size - m_position; }

        bool isLittleEndian() const;

        void setLittleEndian(bool val);

        /**
         * @brief 将ByteArray中的数据[m_position,m_size)转换为std::string
         * @return
         */
        std::string toString() const;

        /**
         * @brief 将ByteArray中的数据[m_position, m_size)转换成16进制的std::string格式为FF:FF:FF
         * @return
         */
        std::string toHexString() const;

        /**
         * @brief 获取可读取的缓冲，保存成iovec数组
         * @param buffers 保存可读取数据的iovec数组
         * @param len 读取数据的长度，如果len > genReadSize() 则len = getReadSize()
         * @return 返回实际数据的长度
         */
        uint64_t getReadBuffers(std::vector<iovec> &buffers, uint64_t len) const;


        /**
         * @brief 获取可读取的缓存，保存成iovec数组，从position位置开始
         * @param buffers 保存可读取的内存的iovec数组
         * @param len 读取数据的长度，如何len > getReadSize() 则 len = getReadSize()
         * @param position 读取数据的位置
         * @return 实际数据的长度
         */
        uint64_t getReadBuffers(std::vector<iovec> &buffers, uint64_t len, uint64_t position) const;

        /**
         * @brief 获取可写入的缓存，保存成iovec数组
         * @param buffers 保存可写入的内存的iovec数组
         * @param len 写入的长度
         * @return 返回实际长度
         */
        uint64_t getWriteBuffers(std::vector<iovec> &buffers, uint64_t len);

    private:

        /**
         * @brief 增加容量
         * @details 就是增加节点的数量
         * @param size
         */
        void addCapacity(size_t size);

        size_t getCapacity() const { return m_capacity - m_position; }

    private:
        /// 每一个节点的大小
        size_t m_baseSize;
        /// 容量
        size_t m_capacity;
        /// 当前的操作的节点的位置
        size_t m_position;
        /// 当前数据的大小
        size_t m_size;
        /// 字节序，默认为网络打端序
        int8_t m_endian;

        /// 根节点
        Node *m_root;
        /// 当前节点
        Node *m_cur;


    };
}// namespace yuechuan


#endif //__YUECHUAN_B
// YTEARRAY_H
